Categories
JavaScript Best Practices

Adopting Better JavaScript Code Style — Error Handling and Comparisons

Spread the love

The way we write JavaScript can always be improved.

In this article, we’ll look at how to improve our code styles by improving the basic syntax we use in our code.

Error Handling

Error handling should be kept as simple as possible. If we encounter an error when running a function, we should throw an error.

For instance, we can throw an error if we encounter an invalid value as follows:

const greet = (name) => {
  if (typeof name !== 'string') {
    throw new Error('name not provided');
  }
  return `hi ${name}`;
}

In the code above, we check if name is a string. If it isn’t, then we throw an error.

Otherwise, we return the string with the greeting.

This is better than the alternative, which is returning error values:

const greet = (name) => {
  if (typeof name !== 'string') {
    return '';
  }
  return `hi ${name}`;
}

In the code above, we returned an empty string instead of throwing an error, so we hid the error from the developer using the function and the user.

It’s very easy to overlook error values since they aren’t immediately obvious. Unlike errors, which will show in the console immediately, we don’t see error values until we’re debugging.

This makes debugging harder since error values don’t show in the console.

Then if we need to handle errors, we can use the try...catch block as follows:

const greet = (name) => {
  if (typeof name !== 'string') {
    throw new Error('name not provided');
  }
  return `hi ${name}`;
}

try {
  greet();
} catch (ex) {
  console.log(ex);
}

In the code above, we have the following try...catch block:

try {
  greet();
} catch (ex) {
  console.log(ex);
}

Then since we didn’t pass in any argument into the greet function when we call it, we get an error in the console from the catch block.

To handle it better, we can choose to display an error message or something like that.

For async code, we should use promises as much as possible. Promises are rejected if errors are encountered, and like throwing errors in synchronous code, they also show up in the console.

For instance, if we have a promise that’s rejected as follows:

Promise.reject('error');

We can call catch on it to catch the error and handle it as follows:

Promise.reject('error')
  .catch(err => console.log(err));

In the code above, we called the catch method and in the catch method call, we passed in a callback to log the error.

Then we can get see errors and handle them in async code easily.

With the async and await syntax, we can catch errors from rejected promises as follows:

(async () => {
  try {
    await Promise.reject('error');
  } catch (err) {
    console.log(err);
  }
})();

As we can see, we just use try...catch like we did with synchronous code if we use the async and await syntax.

Comparison

=== and !== are always better than == and != .

This is because === and !== do not coerce the data types of their operands before doing the comparison.

On the other hand, == and != do data type coercion on their operands before doing any comparison.

By skipping typing one character, we introduced lots of headaches for ourselves because of the automatic type coercion.

Therefore, we should always just type the extra = sign and reduce the chances of bugs significantly.

To keep conditional expressions simple, we should use shortcuts as much as possible.

For instance, the following is bad:

if (condition === true) {
  // ...
}

This is bad because we don’t need to compare against true to check if something is true .

Instead, we should write:

if (condition) {
  // ...
}

Then we check if condition is truthy or not, which includes the value true .

If we use ternary expressions, we shouldn’t nest them. For instance, the following is good:

const foo = cond ? 'foo' : 'bar';

This is because we only have one condition cond and return 'foo' is cond is true and 'bar' otherwise.

However, we shouldn’t nest multiple ternary expressions together. So the following is bad:

const foo = cond1 ? cond2 ? 'foo' : 'bar' : 'baz';

Nesting makes the expression hard to understand. We’ve to put in the parentheses in our brains to compute the value in our heads so that we can understand what it’s doing.

Conclusion

Error handling is nest done by throwing errors and then handling them by catching them.

For async code, we can reject promises and then call catch or use the catch block to handle errors.

For conditionals, we should use the shortest way if we can. We also shouldn’t nest ternary expressions.

To do comparisons, we should use === and !== for comparisons so that we don’t have to worry about type coercions.

By John Au-Yeung

Web developer specializing in React, Vue, and front end development.

Leave a Reply

Your email address will not be published. Required fields are marked *